using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem;
using UnityEngine.Events;
using Unity.Entities;
using System;
using System.Reflection;

namespace LS
{
    public enum InputPhase
    {
        Non,
        Start,
        Update,
        End
    }

    public enum GestureState
    {
        Non,
        Hold,
        Moved,
        Tap,
        LongTap,
    }

    public class PlayerInputHolder : MonoBehaviour
    {
        public InputSetting Setting;
        public List<PlayerInputEvent> InputEvents;

        private List<InputState> m_InputStates = new List<InputState>();
        private Dictionary<string, InputState> m_InputStatesNameMap = new Dictionary<string, InputState>();
        private Dictionary<Type, InputState> m_InputStatesTypeMap = new Dictionary<Type, InputState>();

        private List<InputState> m_InputStatesTempList = new List<InputState>();

        private void Awake()
        {
            RegistInputState<EscState>();
            RegistInputState<LeftClickState>();
            RegistInputState<RightClickState>();
            RegistInputState<LeftSwipeState>();
        }

        private void Update()
        {
            m_InputStatesTempList.Clear();
            m_InputStatesTempList.AddRange(m_InputStates);
            for (int i = 0; i < m_InputStatesTempList.Count; i++)
            {
                var state = m_InputStatesTempList[i];
                state.OnUpdate();
            }

            for (int i = 0; i < InputEvents.Count; i++)
            {
                if (TryGetInputState(InputEvents[i].InputName,out var state))
                {
                    if (state.IsPressed)
                    {
                        InputEvents[i].Events.Invoke(this,state);
                    }
                }
            }
        }

        public bool HasState(string inputName)
        {
            return m_InputStatesNameMap.ContainsKey(inputName);
        }

        public bool HasState<T>()
        {
            return m_InputStatesTypeMap.ContainsKey(typeof(T));
        }

        public bool HasState(InputState inputState)
        {
            return m_InputStatesTypeMap.ContainsKey(inputState.GetType());
        }

        public T GetInputState<T>()
            where T: InputState
        {
            var type = typeof(T);
            return m_InputStatesTypeMap[type] as T;
        }

        public InputState GetInputState(Type type)
        {
            return m_InputStatesTypeMap[type];
        }

        public InputState GetInputState(string inputName)
        {
            return m_InputStatesNameMap[inputName];
        }

        public T GetInputState<T>(string inputName)
            where T: InputState
        {
            return m_InputStatesNameMap[inputName] as T;
        }

        public bool TryGetInputState<T>(out T inputState) where T : InputState
        {
            inputState = default;
            if (m_InputStatesTypeMap.TryGetValue(typeof(T),out var state))
            {
                inputState = state as T;
                return true;
            }
            return false;
        }

        public bool TryGetInputState(Type stateType,out InputState inputState)
        {
            inputState = default;
            if (m_InputStatesTypeMap.TryGetValue(stateType, out var state))
            {
                inputState = state;
                return true;
            }
            return false;
        }

        public bool TryGetInputState(string inputName, out InputState inputState)
        {
            inputState = default;
            if (m_InputStatesNameMap.TryGetValue(inputName, out var state))
            {
                inputState = state;
                return true;
            }
            return false;
        }

        public bool TryGetInputState<T>(string inputName, out T inputState) where T : InputState
        {
            inputState = default;
            if (m_InputStatesNameMap.TryGetValue(inputName, out var state))
            {
                inputState = state as T;
                return true;
            }
            return false;
        }

        public T RegistInputState<T>()
            where T: InputState
        {
            Type type = typeof(T);
            if (m_InputStatesTypeMap.ContainsKey(type))
            {
                throw new Exception($"Input State with Type: {type.FullName} is already exist!");
            }
            T inputState = Activator.CreateInstance(type, Setting) as T;
            Internal_RegistInputState(inputState);
            return inputState;
        }

        public InputState RegistInputState(Type type)
        {
            if (m_InputStatesTypeMap.ContainsKey(type))
            {
                throw new Exception($"Input State with Type: {type.FullName} is already exist!");
            }
            InputState inputState = Activator.CreateInstance(type, Setting) as InputState;
            Internal_RegistInputState(inputState);
            return inputState;
        }

        public void RegistInputState(InputState inputState)
        {
            Internal_RegistInputState(inputState);
        }

        public void UnregistInputState(string inputName)
        {
            if (m_InputStatesNameMap.TryGetValue(inputName, out var state))
            {
                Internal_RegistInputState(state);
            }
        }

        public void UnregistInputState<T>()
            where T : InputState
        {
            Type type = typeof(T);
            if (m_InputStatesTypeMap.TryGetValue(type,out var state))
            {
                Internal_RegistInputState(state);
            }
        }

        public void UnregistInputState(Type type)
        {
            if (m_InputStatesTypeMap.TryGetValue(type, out var state))
            {
                Internal_RegistInputState(state);
            }
        }

        public void UnregistInputState(InputState inputState)
        {
            Internal_UnregistInputState(inputState);
        }

        private void Internal_RegistInputState(InputState inputState)
        {
            Type stateType = inputState.GetType();
            if (m_InputStatesTypeMap.ContainsKey(stateType))
            {
                throw new Exception($"Input State with Type: {stateType.FullName} is already exist!");
            }
            m_InputStates.Add(inputState);
            m_InputStatesTypeMap.Add(stateType,inputState);
            m_InputStatesNameMap.Add(inputState.InputName, inputState);
        }

        private void Internal_UnregistInputState(InputState inputState)
        {
            Type stateType = inputState.GetType();
            m_InputStates.Remove(inputState);
            m_InputStatesTypeMap.Remove(stateType);
            m_InputStatesNameMap.Remove(inputState.InputName);
        }

        [System.Serializable]
        public class PlayerInputEvent
        {
            public string InputName;
            public UnityEvent<PlayerInputHolder, InputState> Events;
        }

        [System.Serializable]
        public class InputSetting
        {
            public float MultipleSelectionMinDistance = 5;
            public float TapDuration = 0.4f;
        }

        public abstract class InputState
        {
            public InputSetting Setting { get; private set; }

            public abstract string InputName { get; }
            public abstract InputPhase Phase { get; }
            public abstract bool IsPressed { get; }
            public abstract float Duration { get; }

            public InputState(InputSetting setting)
            {
                this.Setting = setting;
            }

            public abstract void OnUpdate();
        }
    }
}
